﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EF = Com.Oxilly.Qsb.Core.SDK.EF;
using System.Threading;
using System.Reflection;
using System.IO;
using Com.Oxilly.Qsb.Core.SDK.EF;


namespace Com.Oxilly.Qsb.Core.Managers
{
    /// <summary>
    /// The Extensionmanager is respensoble to keep track off all installed extensions.
    /// It will delegate all events to the subscribing extensions. 
    /// </summary>

    class ExtensionManager : ManagerBase
    {
        private Engine engine;

        private List<EF.IQApp> qApps = new List<EF.IQApp>();

        public ExtensionManager(Engine parent)
        {
            this.engine = parent;
            this.engine.OnQsbEvent += new EventHandler<SDK.Events.QsbEventArgs>(OnQsbEvent);  
        }

        /// <summary>
        /// Handels all QsbEvents and Invokes the extensions each on their own thread.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnQsbEvent(object sender, SDK.Events.QsbEventArgs e)
        {
            foreach (var qApp in qApps)
            {
                Thread t = new Thread(new ParameterizedThreadStart(ProcessQsbEventOnThread));
                t.Start(new ExtensionParamterWrapper
                {
                    QApp = qApp,
                    QsbEventArgs = e,
                    Api = this.engine.Api
                });
            }
        }

        private static void ProcessQsbEventOnThread(object args) 
        {
            ExtensionParamterWrapper w = (ExtensionParamterWrapper)args;
            w.QApp.ProcessQsbEvent(w.Api, w.QsbEventArgs);                       
        }

        private void InstallExtension(EF.QAppBase extension)
        {
            //Prevent from adding the same qApp (by Type) more than once
            if (qApps.Find(e => e.GetType() == extension.GetType())==null)
            {
                qApps.Add(extension);
                this.engine.Log(String.Format(
                    "The extension '{0}' succefully installed.",
                    extension.Name));
            }
            else 
            {
                this.engine.Log(String.Format(
                    "The extension '{0}' was already installed.",
                    extension.Name), System.Diagnostics.SourceLevels.Error);             
            }
        }

        internal void ProcessMessage(SDK.API.QsbMessage msg)
        {
            Log(String.Format("Message from {0} received. {1}({2}) ", msg.MessageSender, msg.MessageType,  msg.Description));           
            foreach (var qApp in this.qApps)
            {
                Thread t = new Thread(new ParameterizedThreadStart(ProcessQsbMessageOnThread));
                t.Name = String.Format("{0} [{0}]", qApp.Name, msg.Number);
                t.Start(new ExtensionParamterWrapper
                {
                    QApp = qApp,
                    QsbMessage = msg,
                    Api = this.engine.Api
                });
            }
        }

        private static void ProcessQsbMessageOnThread(object args)
        {
            ExtensionParamterWrapper w = (ExtensionParamterWrapper)args;
            w.QApp.ProcessQsbMessage(w.Api, w.QsbMessage);
        }

        protected void Log(string msg, System.Diagnostics.SourceLevels level = System.Diagnostics.SourceLevels.Verbose)
        {
            this.engine.Log(new SDK.Events.LogEventArgs
            {
                Level = level,
                Message = msg,
                Source = this.GetType().FullName
            });

        }

        internal void ScanForExtensions()
        {
            //Scan for dll's in a configured directory, use reflextion to find Extensions

            var fullPath = Assembly.GetExecutingAssembly().Location; 
            var path = fullPath.Substring(0, fullPath.LastIndexOf(@"\"));
            var qAppPath = path + @"\..\QApps\";
            Log(String.Format("Klaarmaken voor scannen QApps in '{0}'.", qAppPath));
            var dirs = Directory.GetDirectories(qAppPath);
            foreach (var dir in dirs)
            {
                Log(String.Format("Bezig met scannen van directory '{0}'.", dir));
                var dlls = Directory.GetFiles(dir, @"*.dll");
                foreach (var dll in dlls)
                {
                    Log(String.Format("Bezig met scannen van ddl '{0}'.", dll));

                    var assembly = Assembly.LoadFrom(dll);
                    var qAppTypes =  assembly.GetTypes().Where(t => typeof(Com.Oxilly.Qsb.Core.SDK.EF.QAppBase).IsAssignableFrom(t));
                    foreach (var qAppType in qAppTypes)
                    {
                        try
                        {
                            Log(String.Format("Bezig met aanmaken QApp '{0}'.", qAppType.FullName));
                            var qApp = Activator.CreateInstance(qAppType) as QAppBase;
                            InstallExtension(qApp);
                        }
                        catch(Exception ex)
                        {
                            Log(ex.Message, System.Diagnostics.SourceLevels.Error);
                        }
                    }
                }
            }
        }

        internal override void ReleaseResources()
        {
            foreach (var qApp in qApps)
            {
                qApp.ReleaseResources();
            }
        }

        internal void StartUp()
        {
            foreach (var qApp in qApps)
            {
                qApp.StartUp();
            }
        }
    }
}
